using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace PlanetViewer
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class PlanetViewer : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Effect effect;
        BufferedVerts vBuffer;

        public DebugMessageGroup debugMessage;
        private KeyboardState oldKeyboardState;

        private int _sector = 7;
        private int _sector2 = 0;

        private Color _bColor;

        private Planet _activePlanet;
        public Planet ActivePlanet { get { return this._activePlanet; } }

        public Controls Player1;

        private Camera Cam;

        public PlanetViewer()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            graphics.PreferredBackBufferHeight = 900;
            graphics.PreferredBackBufferWidth = 1600;

            IsMouseVisible = true; //Mouse cursor visible on screen.
        }

        private Texture2D Structure001;

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            _bColor = Color.LightBlue;
            _bColor.R = (byte)187;
            _bColor.G = (byte)233;
            _bColor.B = (byte)248;

            vBuffer = new BufferedVerts(graphics.GraphicsDevice); //Create vertex buffer to hold planet background information (doesn't change much)

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            debugMessage = new DebugMessageGroup(this);

            //debugMessage.Message("message", "This message will self destruct in 6 seconds", DebugTextPosition.LowerRight,Vector2.Zero,1f,Color.White,6000);

            effect = Content.Load<Effect>("effects");

            Structure001 = Content.Load<Texture2D>("Structure001");

            int radius = 800;
            int depth = 40;
            int layers = 10;
            float roughness = 0.3f;

            this._activePlanet = new Planet(this, radius, depth, layers, roughness);

            this.Cam = new Camera(new Vector2(radius, 0), depth, GraphicsDevice.Viewport.AspectRatio);

            Player1 = new Controls(this, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, Cam);

            vBuffer.UpdateBuffer(); //Loads TerrainDrawMask vertex data into buffer.
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState state = Keyboard.GetState();
            //Cam.Angle += 0.0005f;
            Player1.Update();

            this._activePlanet.Terrain.DrawMask.Update();

            if (Player1.Angle != Player1._pAngle)
            {
                float ang = 2 * MathHelper.Pi - Player1.Angle;

                int sec = (int)Math.Floor(ang / MathHelper.PiOver4 - 0.5);
                if (sec < 0) sec += 8; else if (sec > 7) sec -= 8;

                _sector = sec % 8;

                _sector2 = (_sector + 1) % 8;

                float sine = 0.5f * (1 - (float)Math.Cos(Player1.Angle + MathHelper.Pi));

                _bColor.R = (byte)(15 + (int)(172 * sine));
                _bColor.G = (byte)(30 + (int)(203 * sine));
                _bColor.B = (byte)(55 + (int)(193 * sine));

            }

            //debugMessage.Message("LOD", "LOD: " + this._activePlanet.Terrain.LOD.ToString(), DebugTextPosition.UpperLeft);

            debugMessage.Message("ShowAngle", "Mouse Spine: " + Player1.MouseSpine.ToString(), DebugTextPosition.Top);

            debugMessage.Message("Mode", "Mode: " + Player1.GetModeName(), DebugTextPosition.UpperRight);

            if (state.IsKeyDown(Keys.R) && oldKeyboardState.IsKeyUp(Keys.R))
            {
                this.LoadContent();
            }

            oldKeyboardState = state;
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(_bColor);
            //GraphicsDevice.Clear(Color.IndianRed);

            RasterizerState rs = new RasterizerState();
            rs.CullMode = CullMode.None;
            rs.FillMode = FillMode.Solid;
            rs.FillMode = FillMode.WireFrame;
            GraphicsDevice.RasterizerState = rs;

            GraphicsDevice.BlendState = BlendState.NonPremultiplied;

            

            effect.CurrentTechnique = effect.Techniques["ColoredNoShading"];
            
            effect.Parameters["xView"].SetValue(Cam.ViewRotMatrix * Cam.ViewPosMatrix);
            effect.Parameters["xProjection"].SetValue(Cam.ProjectionMatrix);
            effect.Parameters["xWorld"].SetValue(Matrix.Identity);

            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                
                pass.Apply();
                GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, this._activePlanet.Terrain.DrawMask.Vertices, 0, this._activePlanet.Terrain.DrawMask.Vertices.Length, this._activePlanet.Terrain.DrawMask.Indices, 0, this._activePlanet.Terrain.DrawMask.Indices.Length / 3, VertexPositionColor.VertexDeclaration);
                //vBuffer.DrawFromBuffer();
            }

            #region debugMessages
            
            spriteBatch.Begin();
            if (Player1._Mode == 1)
            {
                spriteBatch.Draw(Structure001, new Vector2(Player1.MouseState.X, Player1.MouseState.Y), null, Color.White, Player1._mouseAng, new Vector2(Structure001.Width / 2, Structure001.Height), Player1.Zoom * 0.8f, SpriteEffects.None, 0f);
            }
            debugMessage.RenderGroup(spriteBatch);
            spriteBatch.End();
            
            #endregion

            base.Draw(gameTime);
        }

        
    }
}
